// ******************************** 数值 ********************************
// · num 是数值类型，既包含整数，也包含浮点数（⼩数）
//     · int 整数类型（必须是整数）
//     · double 浮点数类型（既可以是整数，也可以是⼩数）
void function1() {
  // 使⽤ int 和 double 的 parse() ⽅法将字符串转换为整型或双浮点型对象
  print(int.parse('42') == 42);
  print(int.parse('0x42') == 66);
  print(double.parse('0.50') == 0.5);
  // 使⽤ num 的 parse() ⽅法，该⽅法可能会创建⼀个整型，否则为浮点型对象
  print(num.parse('42') is int);
  print(num.parse('0x42') is int);
  print(num.parse('0.50') is double);
  // 通过添加 radix 参数，指定整数的进制基数：
  print(int.parse('42', radix: 16) == 66);
  // 使⽤ toString() ⽅法将整型或双精度浮点类型转换为字符串类型
  // 使⽤ toStringAsFixed(). 指定⼩数点右边的位数。
  // 使⽤ toStringAsPrecision(): 指定字符串中的有效数字的位数。
  // 将 int 转成 string
  print(42.toString() == '42');
  // 将 double 转成 string.
  print(123.456.toString() == '123.456');
  // 指定⼩数位数
  print(123.456.toStringAsFixed(2) == '123.46');
  // 转换科学计数法
  print(123.456.toStringAsPrecision(2) == '1.2e+2');
  print(double.parse('1.2e+2') == 120.0);
}

// ******************************** 字符串 ********************************
void function2() {
  // 声明字符串
  // 方式 1： 用var声明，然后Dart自动推断字符串类型
  var str1 = "this is string 1";
  var str2 = "this is string 2";
  // 方式 2： 用String声明
  String str3 = "this is string 3";
  String str4 = "this is string 4";
  // 方式 3： 三个引号声明可换行的字符串
  String str5 = """this is string 5""";

  // 字符串拼接
  print("$str1 $str2");
  print(str1 + " " + str2);

  // 字符串API
  // String 定义了例如 split(), contains(), startsWith(), endsWith() 等⽅法
  // 包含
  print('Never odd or even'.contains('odd'));
  // 匹配开头
  print('Never odd or even'.startsWith('Never'));
  // 匹配结尾
  print('Never odd or even'.endsWith('even'));
  // 查找指定字符的索引（位置）
  print('Never odd or even'.indexOf('odd') == 6);

  // 字符串常见操作
  // 求⼦串
  assert('Never odd or even'.substring(6, 9) == 'odd');
  // 分割字符串
  var parts = 'structured web apps'.split(' ');
  assert(parts.length == 3);
  assert(parts[0] == 'structured');
  // 根据索引取值
  assert('Never odd or even'[0] == 'N');
  // 通过空字符将字符串分割
  for (var char in 'hello'.split('')) {
    print(char);
  }
  // 获取字符串的 UTF-16 编码
  var codeUnitList = 'Never odd or even'.codeUnits.toList();
  assert(codeUnitList[0] == 78);
  // ⼤⼩写转换
  // 转成⼤写
  assert('hello world'.toUpperCase() == 'HELLO WORLD');
  // 转成⼩写
  assert('HELLO WORLD'.toLowerCase() == 'hello world');

  // 使⽤ trim() 移除⾸尾空格。使⽤ isEmpty 检查⼀个字符串是否为空（⻓度为0）。
  // 修剪字符串
  assert(' hello '.trim() == 'hello');
  // 判断字符串是否为空
  assert(''.isEmpty);
  // 判断字符串不为空
  assert(' '.isNotEmpty);

  // 替换字符串 字符串是不可变的对象，也就是说字符串可以创建但是不能被修改。
  // 没有⼀个⽅法实际的改变了字符串 的状态。例如，⽅法 replaceAll() 返回⼀个新字符串，
  // 并没有改变原始字符串：
  var s1 = 'Hello, NAME!';
  var s2 = s1.replaceAll('NAME', 'Bob');
  // s1 没有变
  assert(s2 != s1);

  // 构造字符串 要以代码⽅式⽣成字符串，可以使⽤ StringBuffer。
  // writeAll() 的第⼆个参数为可选参数，⽤来指定 分隔符
  var sb = StringBuffer();
  sb
    ..write('Use a StringBuffer for ')
    ..writeAll(['efficient', 'string', 'creation'], ' ')
    ..write('.');
  var fullString = sb.toString();
  assert(fullString == 'Use a StringBuffer for efficient string tion.');

  // 正则表达式 RegExp 类提供与 JavaScript 正则表达式相同的功能。
  // 使⽤正则表达式可以对字符串进⾏⾼效搜索和模 式匹配。
  var numbers = RegExp(r'\d+');
  var allCharacters = 'llamas live fifteen to twenty years';
  var someDigits = 'llamas live 15 to 20 years';
  assert(!allCharacters.contains(numbers));
  assert(someDigits.contains(numbers));
  var exedOut = someDigits.replaceAll(numbers, 'XX');
  assert(exedOut == 'llamas live XX to XX years');

  // 获取正则匹配结果
  // 是否匹配
  assert(numbers.hasMatch(someDigits));
  // 循环输出所有的匹配结果
  for (var match in numbers.allMatches(someDigits)) {
    print(match.group(0));
  }
}

// ******************************** 布尔 ********************************

// ******************************** List ********************************
// list 是参数化类型，因此可以指定 list 应该包含的元素类型
void function4() {
  var fruits = List<String>.filled(5, "0", growable: true);
  fruits.add("apples");
  print(fruits.length);
  print(fruits);

  var foods = List.empty();
  foods.add("apples");
  foods.add(1);
  print(foods);

  var users = List.empty(growable: true);
  users.add("Jack");
  users.add(2);
  print(users);

  var lsUser = [];
  lsUser.add("Jack");
  lsUser.add(2);
  print(lsUser);

  // Dart 在 2.3 引⼊了 扩展操作符（ ... ）和 null-aware 扩展操作符（ ...? ）
  var list = [1, 2, 3];
  var list2 = [0, ...list];
  print(list2);

  // Dart 在 2.3 还同时引⼊了 Collection If 和 Collection For，
  // 在构建集合时，可以使⽤条件判断（ if ） 和循环（ for ）。
  var promoActive = true;
  var nav = ['Home', 'Furniture', 'Plants', if (promoActive) 'Outlet'];
  print(nav);

  var listOfInts = [1, 2, 3];
  var listOfStrings = ['#0', for (var i in listOfInts) '#$i'];
  print(listOfStrings);
}

// ******************************** Set ********************************
// 在 Dart 中，Set 是⼀个⽆序的，元素唯⼀的集合。因为⼀个 set 是⽆序的，所以⽆法通过下标（位置） 获取 set 中的元素
void function5() {
  var numbers = Set();
  numbers.addAll([1, 2, 3]);
  numbers.add(1); // "1" ???
  print(numbers);

  // 求两个集合的交集
  var even = Set.from([2, 4]);
  var intersection = numbers.intersection(even);
  print(intersection);

  // 集合的合并通过 union() 函数完成
  print(numbers.union(even));
}

// ******************************** Map ********************************
// Map 是⼀个⽆序的 key-value （键值对）集合
void function6() {
  // ⼀般 Map 的键（key）是字符串类型
  var stars = {
    'boy': ['肖战', '王⼀博', '⿅晗'],
    'girl': ['杨幂', '李⼀桐', '王晓晨']
  };

  // 以构造函数的⽅式声明 Map
  var myMap = Map();

  // 可以指定 Map 的键（key）和值（value）的数据类型
  var mapName = Map<int, String>();

  // 如果当且仅当该 key 不存在于 map 中，且要为这个 key 赋值，可使⽤ putIfAbsent（） ⽅法。
  // 该⽅法 需要⼀个⽅法返回这个 value。
  myMap.putIfAbsent('key', () => 'value');
  print(myMap);
}

void main() {
  function5();
}
